<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>

                    <h4>使用模板</h4>
                    <div class="x-wiki-info"><span>676次阅读</span></div>
                    <hr style="border-top-color:#ccc" />
                    <div class="x-wiki-content x-content"><p>Web框架把我们从WSGI中拯救出来了。现在，我们只需要不断地编写函数，带上URL，就可以继续Web App的开发了。</p>
<p>但是，Web App不仅仅是处理逻辑，展示给用户的页面也非常重要。在函数中返回一个包含HTML的字符串，简单的页面还可以，但是，想想新浪首页的6000多行的HTML，你确信能在Python的字符串中正确地写出来么？反正我是做不到。</p>
<p>俗话说得好，不懂前端的Python工程师不是好的产品经理。有Web开发经验的同学都明白，Web App最复杂的部分就在HTML页面。HTML不仅要正确，还要通过CSS美化，再加上复杂的JavaScript脚本来实现各种交互和动画效果。总之，生成HTML页面的难度很大。</p>
<p>由于在Python代码里拼字符串是不现实的，所以，模板技术出现了。</p>
<p>使用模板，我们需要预先准备一个HTML文档，这个HTML文档不是普通的HTML，而是嵌入了一些变量和指令，然后，根据我们传入的数据，替换后，得到最终的HTML，发送给用户：</p>
<p><img src="http://www.liaoxuefeng.com/files/attachments/001400339839622665127663fb840b5870864895b103c2f000" alt="mvc-seq"></p>
<p>这就是传说中的MVC：Model-View-Controller，中文名“模型-视图-控制器”。</p>
<p>Python处理URL的函数就是C：Controller，Controller负责业务逻辑，比如检查用户名是否存在，取出用户信息等等；</p>
<p>包含变量<code>{{ name }}</code>的模板就是V：View，View负责显示逻辑，通过简单地替换一些变量，View最终输出的就是用户看到的HTML。</p>
<p>MVC中的Model在哪？Model是用来传给View的，这样View在替换变量的时候，就可以从Model中取出相应的数据。</p>
<p>上面的例子中，Model就是一个<code>dict</code>：</p>
<pre><code>{ &#39;name&#39;: &#39;Michael&#39; }
</code></pre><p>只是因为Python支持关键字参数，很多Web框架允许传入关键字参数，然后，在框架内部组装出一个<code>dict</code>作为Model。</p>
<p>现在，我们把上次直接输出字符串作为HTML的例子用高端大气上档次的MVC模式改写一下：</p>
<pre><code>from flask import Flask, request, render_template

app = Flask(__name__)

@app.route(&#39;/&#39;, methods=[&#39;GET&#39;, &#39;POST&#39;])
def home():
    return render_template(&#39;home.html&#39;)

@app.route(&#39;/signin&#39;, methods=[&#39;GET&#39;])
def signin_form():
    return render_template(&#39;form.html&#39;)

@app.route(&#39;/signin&#39;, methods=[&#39;POST&#39;])
def signin():
    username = request.form[&#39;username&#39;]
    password = request.form[&#39;password&#39;]
    if username==&#39;admin&#39; and password==&#39;password&#39;:
        return render_template(&#39;signin-ok.html&#39;, username=username)
    return render_template(&#39;form.html&#39;, message=&#39;Bad username or password&#39;, username=username)

if __name__ == &#39;__main__&#39;:
    app.run()
</code></pre><p>Flask通过<code>render_template()</code>函数来实现模板的渲染。和Web框架类似，Python的模板也有很多种。Flask默认支持的模板是<a href="http://jinja.pocoo.org/">jinja2</a>，所以我们先直接安装jinja2：</p>
<pre><code>$ easy_install jinja2
</code></pre><p>然后，开始编写jinja2模板：</p>
<h3 id="home-html">home.html</h3>
<p>用来显示首页的模板：</p>
<pre><code>&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;Home&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1 style=&quot;font-style:italic&quot;&gt;Home&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><h3 id="form-html">form.html</h3>
<p>用来显示登录表单的模板：</p>
<pre><code>&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;Please Sign In&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  {% if message %}
  &lt;p style=&quot;color:red&quot;&gt;{{ message }}&lt;/p&gt;
  {% endif %}
  &lt;form action=&quot;/signin&quot; method=&quot;post&quot;&gt;
    &lt;legend&gt;Please sign in:&lt;/legend&gt;
    &lt;p&gt;&lt;input name=&quot;username&quot; placeholder=&quot;Username&quot; value=&quot;{{ username }}&quot;&gt;&lt;/p&gt;
    &lt;p&gt;&lt;input name=&quot;password&quot; placeholder=&quot;Password&quot; type=&quot;password&quot;&gt;&lt;/p&gt;
    &lt;p&gt;&lt;button type=&quot;submit&quot;&gt;Sign In&lt;/button&gt;&lt;/p&gt;
  &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><h3 id="signin-ok-html">signin-ok.html</h3>
<p>登录成功的模板：</p>
<pre><code>&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;Welcome, {{ username }}&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;p&gt;Welcome, {{ username }}!&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><p>登录失败的模板呢？我们在<code>form.html</code>中加了一点条件判断，把<code>form.html</code>重用为登录失败的模板。</p>
<p>最后，一定要把模板放到正确的<code>templates</code>目录下，<code>templates</code>和<code>app.py</code>在同级目录下：</p>
<p><img src="http://www.liaoxuefeng.com/files/attachments/001400341074577704e1ff7d52246dab80eb4992d12fcd1000" alt="mvc-dir"></p>
<p>启动<code>python app.py</code>，看看使用模板的页面效果：</p>
<p><img src="http://www.liaoxuefeng.com/files/attachments/00140034078412854f0d48a3c3649f99358ae44f6ca7405000" alt="mvc-form"></p>
<p>通过MVC，我们在Python代码中处理M：Model和C：Controller，而V：View是通过模板处理的，这样，我们就成功地把Python代码和HTML代码最大限度地分离了。</p>
<p>使用模板的另一大好处是，模板改起来很方便，而且，改完保存后，刷新浏览器就能看到最新的效果，这对于调试HTML、CSS和JavaScript的前端工程师来说实在是太重要了。</p>
<p>在Jinja2模板中，我们用<code>{{ name }}</code>表示一个需要替换的变量。很多时候，还需要循环、条件判断等指令语句，在Jinja2中，用<code>{% ... %}</code>表示指令。</p>
<p>比如循环输出页码：</p>
<pre><code>{% for i in page_list %}
    &lt;a href=&quot;/page/{{ i }}&quot;&gt;{{ i }}&lt;/a&gt;
{% endfor %}
</code></pre><p>如果<code>page_list</code>是一个list：<code>[1, 2, 3, 4, 5]</code>，上面的模板将输出5个超链接。</p>
<p>除了Jinja2，常见的模板还有：</p>
<ul>
<li><p><a href="http://www.makotemplates.org/">Mako</a>：用<code>&lt;% ... %&gt;</code>和<code>${xxx}</code>的一个模板；</p>
</li>
<li><p><a href="http://www.cheetahtemplate.org/">Cheetah</a>：也是用<code>&lt;% ... %&gt;</code>和<code>${xxx}</code>的一个模板；</p>
</li>
<li><p><a href="https://www.djangoproject.com/">Django</a>：Django是一站式框架，内置一个用<code>{% ... %}</code>和<code>{{ xxx }}</code>的模板。</p>
</li>
</ul>
<h3 id="-">小结</h3>
<p>有了MVC，我们就分离了Python代码和HTML代码。HTML代码全部放到模板里，写起来更有效率。</p>
</div>

                    <hr style="border-top-color:#ccc" />

                    